﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using d = System.Diagnostics;
using EnvDTE;
using UnitTests.Classes;
using System.Reflection;
using AbstraX.ServerInterfaces;
using AssemblyProvider.Web.Entities;
using AbstraX.Generators;
using System.ComponentModel.DataAnnotations;
using System.ServiceModel.DomainServices.Hosting;
using SolutionFramework.Entities;

namespace ConsoleApplication4
{
    class Program
    {
        class Test
        {
            public System.Type GetProvider(System.Type BaseType)
            {
                foreach (System.Type type in BaseType.Assembly.GetTypes())
                {
                    if (type.GetCustomAttributes(true).AsQueryable().Cast<Attribute>().Any(a => a is EnableClientAccessAttribute))
                    {
                        return type;
                    }
                }

                return null;
            }

            public NodeType? GetElementType(System.Type BaseType)
            {
                foreach (System.Type interfaceType in BaseType.GetInterfaces())
                {
                    if (interfaceType.Name == "IRoot")
                    {
                        return NodeType.Root;
                    }
                    else if (interfaceType.Name == "IElement")
                    {
                        return NodeType.Element;
                    }
                }

                var collectionProperties = new List<PropertyInfo>();

                return null;
            }

            public List<System.Type> GetElementTypes(System.Type BaseType)
            {
                List<System.Type> elementTypes = new List<System.Type>();

                foreach (System.Type type in BaseType.Assembly.GetTypes())
                {
                    foreach (System.Type interfaceType in type.GetInterfaces())
                    {
                        if (interfaceType.Name == "IElement")
                        {
                            elementTypes.Add(type);
                        }
                    }
                }

                return elementTypes;
            }

            public List<System.Type> GetAttributeTypes(System.Type BaseType)
            {
                List<System.Type> attributeTypes = new List<System.Type>();

                foreach (System.Type type in BaseType.Assembly.GetTypes())
                {
                    foreach (System.Type interfaceType in type.GetInterfaces())
                    {
                        if (interfaceType.Name == "IAttribute")
                        {
                            attributeTypes.Add(type);
                        }
                    }
                }

                return attributeTypes;
            }

            public List<System.Type> GetOperationTypes(System.Type BaseType)
            {
                List<System.Type> operationTypes = new List<System.Type>();

                foreach (System.Type type in BaseType.Assembly.GetTypes())
                {
                    foreach (System.Type interfaceType in type.GetInterfaces())
                    {
                        if (interfaceType.Name == "IOperation")
                        {
                            operationTypes.Add(type);
                        }
                    }
                }

                return operationTypes;
            }

            public void ProcessType(System.Type BaseType)
            {
                System.Type providerType = GetProvider(BaseType);
                NodeType? entityType = GetElementType(BaseType);
                List<System.Type> elementTypes = GetElementTypes(BaseType);
                List<System.Type> attributeTypes = GetAttributeTypes(BaseType);
                List<System.Type> operationTypes = GetOperationTypes(BaseType);
                Dictionary<PropertyInfo, System.Type> elementProperties = new Dictionary<PropertyInfo, System.Type>();
                Dictionary<PropertyInfo, System.Type> attributeProperties = new Dictionary<PropertyInfo, System.Type>();
                Dictionary<PropertyInfo, System.Type> operationProperties = new Dictionary<PropertyInfo, System.Type>();
                var interfaceName = Enum.GetName(typeof(NodeType), entityType);

                foreach (MethodInfo methodInfo in providerType.GetMethods())
                {
                    if (methodInfo.ReturnType.Name == "IQueryable`1")
                    {
                        System.Type typeParm = methodInfo.ReturnType.GetGenericArguments().AsQueryable().Cast<System.Type>().FirstOrDefault();
                    }
                    else
                    {
                        foreach (System.Type interfaceType in methodInfo.ReturnType.GetInterfaces())
                        {
                            if (interfaceType.Name == "IRoot")
                            {
                            }
                        }
                    }
                }
                
                if (entityType != null)
                {
                    foreach (var property in BaseType.GetProperties())
                    {
                        // todo - support other collection types

                        if (property.PropertyType.Name == "List`1" && property.PropertyType.IsGenericType)
                        {
                            var argType = property.PropertyType.GetGenericArguments()[0];

                            if (elementTypes.AsEnumerable().Any(e => e.Name == argType.Name))
                            {
                                if (property.GetCustomAttributes(true).AsQueryable().Cast<Attribute>().Any(a => a is AssociationAttribute))
                                {
                                    elementProperties.Add(property, argType);
                                }
                            }
                            else if (attributeTypes.AsEnumerable().Any(e => e.Name == argType.Name))
                            {
                                if (property.GetCustomAttributes(true).AsQueryable().Cast<Attribute>().Any(a => a is AssociationAttribute))
                                {
                                    attributeProperties.Add(property, argType);
                                }
                            }
                            else if (operationTypes.AsEnumerable().Any(e => e.Name == argType.Name))
                            {
                                if (property.GetCustomAttributes(true).AsQueryable().Cast<Attribute>().Any(a => a is AssociationAttribute))
                                {
                                    operationProperties.Add(property, argType);
                                }
                            }
                        }
                    }

                    foreach (var property in BaseType.GetProperties())
                    {
                        string propertyType;

                        if (property.PropertyType.IsGenericType)
                        {
                            var args = String.Join<string>(", ",  property.PropertyType.GetGenericArguments().AsEnumerable().Select(a => a.Name).ToArray<string>());

                            propertyType = property.PropertyType.Name;
                            propertyType = propertyType.Substring(0, propertyType.IndexOf('`'));

                            if (propertyType == "List")
                            {
                                propertyType = "EntitySet";
                            }

                            propertyType = propertyType + "<" + args + ">";
                        }
                        else
                        {
                            propertyType = property.PropertyType.Name;
                        }
                    }

                    foreach (var property in BaseType.GetProperties())
                    {
                        if (property.Name == "RootElements" || property.Name == "ChildElements")
                        {
                            foreach (var childElementType in elementProperties)
                            {
                                var str = childElementType.Key.Name + childElementType.Value.Name;
                            }
                        }
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            //System.Type t = typeof(AssembliesRoot);
            //TemplateEngineHost engine = new TemplateEngineHost(NodeType.Wrapper);

            //var entities = new SolutionFrameworkEntities();

            //foreach (var root in entities.TreeBuilderRoots)
            //{
            //}

            //var output = engine.GenerateCode(t);

            //var test = new Test();

            //test.ProcessType(t);

            //var type = System.Type.GetTypeFromProgID("VisualStudio.DTE.9.0");
            //var obj = System.Activator.CreateInstance(type, true);
            //var dte = (DTE)obj;
            //var solution = dte.Solution;

            //solution.Open(@"C:\ConsoleApplication3\ConsoleApplication3.sln");

            //var project = solution.Projects.Find("ConsoleApplication3");
            //var codeFile = project.ProjectItems.Find(@"AbstraX\Entity.cs");
            //var codeModel = codeFile.FileCodeModel;

            //foreach (CodeElement codeElement in codeModel.CodeElements)
            //{
            //    ProcessElement(codeElement);
            //}

            //solution.Close(false);

            //dte.Quit();

            //Marshal.ReleaseComObject(dte);
        }

        static void ProcessElement(CodeElement codeElement)
        {
            switch (codeElement.Kind)
            {
                case vsCMElement.vsCMElementProperty:

                    var property = (CodeProperty)codeElement;

                    break;

                case vsCMElement.vsCMElementOther:
                case vsCMElement.vsCMElementClass:
                case vsCMElement.vsCMElementFunction:
                case vsCMElement.vsCMElementVariable:
                case vsCMElement.vsCMElementNamespace:
                case vsCMElement.vsCMElementParameter:
                case vsCMElement.vsCMElementAttribute:
                case vsCMElement.vsCMElementInterface:
                case vsCMElement.vsCMElementDelegate:
                case vsCMElement.vsCMElementEnum:
                case vsCMElement.vsCMElementStruct:
                case vsCMElement.vsCMElementUnion:
                case vsCMElement.vsCMElementLocalDeclStmt:
                case vsCMElement.vsCMElementFunctionInvokeStmt:
                case vsCMElement.vsCMElementPropertySetStmt:
                case vsCMElement.vsCMElementAssignmentStmt:
                case vsCMElement.vsCMElementInheritsStmt:
                case vsCMElement.vsCMElementImplementsStmt:
                case vsCMElement.vsCMElementOptionStmt:
                case vsCMElement.vsCMElementVBAttributeStmt:
                case vsCMElement.vsCMElementVBAttributeGroup:
                case vsCMElement.vsCMElementEventsDeclaration:
                case vsCMElement.vsCMElementUDTDecl:
                case vsCMElement.vsCMElementDeclareDecl:
                case vsCMElement.vsCMElementDefineStmt:
                case vsCMElement.vsCMElementTypeDef:
                case vsCMElement.vsCMElementIncludeStmt:
                case vsCMElement.vsCMElementUsingStmt:
                case vsCMElement.vsCMElementMacro:
                case vsCMElement.vsCMElementMap:
                case vsCMElement.vsCMElementIDLImport:
                case vsCMElement.vsCMElementIDLImportLib:
                case vsCMElement.vsCMElementIDLCoClass:
                case vsCMElement.vsCMElementIDLLibrary:
                case vsCMElement.vsCMElementImportStmt:
                case vsCMElement.vsCMElementMapEntry:
                case vsCMElement.vsCMElementVCBase:
                case vsCMElement.vsCMElementEvent:
                case vsCMElement.vsCMElementModule:
                    break;
            }

            ProcessChildren(codeElement);
        }

        static void ProcessChildren(CodeElement codeElement)
        {
            foreach (CodeElement elemChild in codeElement.Children)
            {
                ProcessElement(elemChild);
            }
        }
    }

    public static class DTEExtensions
    {
        public static Project Find(this Projects projects, string name)
        {
            foreach (Project project in projects)
            {
                if (project.Name == name)
                {
                    return project;
                }
            }

            return null;
        }

        public static ProjectItem Find(this ProjectItems projectItems, string name)
        {
            var queue = new Queue<string>(name.Split('\\'));
            var items = projectItems;
            ProjectItem currentItem = null;

            while (queue.Count > 0)
            {
                var subName = queue.Dequeue();

                foreach (ProjectItem item in items)
                {
                    if (item.Name == subName)
                    {
                        currentItem = item;
                        items = currentItem.ProjectItems;
                        break;
                    }
                }
            }

            return currentItem;
        }
    }
}
